---
slug: redux-toolkit #路径，不能重复
title:  redux-toolkit # 标题
authors: [] # 作者，在authors.yml配置
tags: [facebook, hello, docusaurus] # 标签，在tags.yml配置
toc_min_heading_level: 2 # 最小展示标题
toc_max_heading_level: 5 # 最大展示标题
---

##### 最大特点

> 基于切片机制，把reducer和actionCreator合并在一起

```shell
npm install @reduxjs/toolkit react-redux redux-logger

```

### 创建store

1. **`reducer`** reducer,一定要注意没有s
2. `middleware` 中间件,如果不给的话`redux-toolkit`会默认给一个`redux-thunk`
    * `redux-toolkit`根据`package.json`的依赖检查是否安装了`redux-thunk`如果安装了则`getDefaultMiddleware`的返回结果会默认加上`redux-thunk`,这时候再引入自己的`redux-thunk`就报错了
    * 推荐写法:
```js

import {configureStore} from '@reduxjs/toolkit';
import reduxLogger from 'redux-logger';
import {thunk} from 'redux-thunk';
import voteReducer from './features/voteSlice';
const customMiddleWare = [
    reduxLogger,//导入log
    thunk//导入thunk
]
const store = configureStore({
    reducer: {
        vote:voteReducer
    },
    middleware: (getDefaultMiddleware)=>[
        ...customMiddleWare //不要他原本的中间件
    ]
})

export default store;
```


```js title="src/store/index.js"
import {configureStore} from '@reduxjs/toolkit';
import reduxLogger from 'redux-logger';
import voteReducer from './features/voteSlice';

const store = configureStore({
    reducer: {
        vote:voteReducer
    },
    middleware: (getDefaultMiddleware)=>[...getDefaultMiddleware(),reduxLogger]
})

export default store;
```

### 创建切片
> 切片合并了reducer和actionCreator


1. `name` reducer的名称
2. `initialState` 公共状态
3. `reducers` 对公共状态修改的函数
    * `state` 公共状态 内部使用`Immer`处理了不可变,不需要再浅克隆了
    * `action` 传递过来的参数存在`action.payload`里面
4. `export const {upNumIncrement} =  voteSlice.actions;`返回的函数不是内部的那个函数
    * 这个函数调用的返回结果是`{type:'vote/upNumIncrement',payload:undefined}`
        * `type`就是`createSlice`里面的`name`+`/`+函数名,**所以`name`不能重复**
        * `payload`是调用这个函数传进去的参数

```js title="src/store/features/voteSlice.js"
import { createSlice} from "@reduxjs/toolkit";
//切片
const voteSlice = createSlice({
    name: "vote",
    initialState: {
        upNum:0,
        downNum:0
    },
    reducers: {
        upNumIncrement: (state, action) => {
            state.upNum = state.upNum+action.payload;
        },
        upNumDecrement: (state, action) => {
            state.downNum = state.downNum-action.payload;
        },
        upNumReset: (state, action) => {
            state.upNum = 0;
        },
        downNumIncrement: (state, action) => {
          state.downNum = state.downNum+action.payload;
        },
        downNumDecrement: (state, action) => {
            state.downNum = state.downNum-action.payload;
        },
        downNumReset: (state, action) => {
            state.downNum = 0;
        }
    }
});
//从切片中获取actions
export const {upNumIncrement,upNumDecrement,upNumReset,downNumIncrement,downNumDecrement,downNumReset} =  voteSlice.actions;

//从切片中获取reducer
export default voteSlice.reducer;
```


### 给到根组件
> 使用`react-redux`的Provider组件的`store`属性把`store`给到下面的组件


```jsx title="src/index.js"
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import reportWebVitals from './reportWebVitals';
    import {App} from './views/App';
    import {Provider} from 'react-redux';
    import store from './store/index';
    const root = ReactDOM.createRoot(document.getElementById('root'));
    console.log("store", store);
    root.render(
      <>
          <Provider store={toolKitStore}>
              <App/>
          </Provider>
      </>
    )
    reportWebVitals();

```

### 下面组件接收
> 不再使用`react-redux`的`connect`接收，而是使用`react-redux`为`redux-toolkit`提供的hooks接收
1. 解构`react-redux`
    * `useSelector`
        * 获取根组件给到的store
            * `const {公共状态} = useSelector(state=>state['在configureStore.reducer里面定义的key'])`
    * `useDispatch`
        * 派发事件
            * `const dispatch = useDispatch();`
2. `useDispatch派发事件`

```jsx
import {useDispatch} from 'react-redux';
import  {Button} from "antd";
import {upNumIncrement} from 'src/store/features/voteSlice';
//获取派发
const dispatch = useDispatch();

//派发事件,1就是传递的payload
return (
    <Button onClick={()=>dispatch(upNumIncrement(1))}></Button>
)
```

---

```jsx
import React from 'react';
import {Button, Divider} from "antd";
import {useSelector, useDispatch} from 'react-redux';
import {upNumIncrement, downNumIncrement} from 'src/store/toolSlice';
const  ToolKitComp = function (props) {
    const dispatch = useDispatch();
    const {upNum,downNum} = useSelector(state=>state.vote);
    return (
        <>
            <div className={'container'}>
                <h2>React是很棒的框架</h2>
                <div>
                    {upNum+downNum}
                </div>
            </div>
            <div>
                <div>
                    赞同票数:{upNum}
                </div>
                <div>
                    反对票数:{downNum}
                </div>
            </div>
            <Divider/>
            <Button onClick={()=>dispatch(upNumIncrement(1))}>
                投赞同票
            </Button>

            <Button onClick={()=>dispatch(downNumIncrement(1))}>
                投反对票
            </Button>
        </>
    )

};

export default ToolKitComp;

```

### `redux-toolkit`处理异步派发

1. `createAsyncThunk` 根据传递进去的第一个参数`name`生成三个`action`
    * `name/pending` 请求正在进行中
    * `name/fulfilled` 请求已完成
    * `name/rejected` 请求error
2. `createAsyncThunk` 第二个参数要求必须返回`Promise`
    * 上面的几个状态都是`createAsyncThunk`内部在监听返回的promise的状态
3. `extraReducers` 扩展reducer
    * 第一个参数是要监听的`action`
    * 第二个参数
        *  `state` 全局状态,即`initialState`内定义的
        * `action` 传进来的参数
            * `action.payload` 成功时候`fulfilled`传递的参数
            * `action.error` 失败`rejected`传递的参数


```js title="src/store/voteSlice.js"
import { createSlice,createAsyncThunk} from "@reduxjs/toolkit";

const fetchUser = createAsyncThunk('user/fetchUser',async (userId) => {
    const response = await fetch('./toolSlice.js');
    return await response.json();
})
//切片
const toolSlice = createSlice({
    name: "voteSlice",
    initialState: {
        requestState:{
            loading:'idle',
            error:null,
            data:null
        }
    },
    reducers: {
        getData:(state,action)=>{
            return state.requestState.data;
        }
    },
    extraReducers: (builder) => {
        builder.addCase(fetchUser.pending, (state, action) => {
            state.requestState.loading = 'loading';
        })
            .addCase(fetchUser.fulfilled, (state, action) => {
                state.requestState.loading = 'success';
                state.requestState.data = action.payload;
            })
            .addCase(fetchUser.rejected, (state, action) => {
                state.requestState.loading = 'error';
                state.requestState.error = action.error.message;
            })
        ;
    }
});
export const {upNumIncrement, downNumIncrement,fetchUser} = toolSlice.actions;
export default toolSlice.reducer;

```

#### 组件内部使用

1. **必须要通过`useDispatch`得到的结果的函数派发**

```jsx
import React ,{useEffect}from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {fetchUser} from 'src/store/fetures/voteSlice';
const  ToolKitComp = function (props) {
    const dispatch = useDispatch();

    //返回的data不能是对象，因为jsx不能直接渲染对象，我们只是测试而已
    const {requestState:{loading,error,data}} = useSelector(state=>state.vote);
    //开始调用异步方法
    useEffect(() => {
        //必须要通过dispatch派发
    	dispatch(fetchUser(1));
    }, []);
    return (
        <>
            {data}
        </>
    )

};

export default ToolKitComp;


```
